#include <n32g45x_rtc.h>
#include <rtc.h>
#include "log.h"

RTC_DateType  RTC_DateStructure;
RTC_TimeType  RTC_TimeStructure;

RTC_InitType  RTC_InitStructure;
uint32_t SynchPrediv, AsynchPrediv;

void rtc_init(void)
{
    /* Enable the PWR clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);
    /* Allow access to RTC */
    PWR_BackupAccessEnable(ENABLE);

    if (USER_WRITE_BKP_DAT1_DATA != BKP_ReadBkpData(BKP_DAT1) )
    {
        /* Backup data register value is not correct or not yet programmed (when
           the first time the program is executed) */
        LOG_DEBUG("RTC not yet configured...\r\n");

        /* RTC clock source select */
        RTC_CLKSourceConfig(RTC_CLK_SRC_TYPE_LSE, true, true);
        RTC_PrescalerConfig();
        LOG_DEBUG("RTC is configuring...\r\n");

        /* Set initinal Date*/
        RTC_DateStructInit(&RTC_DateStructure);
        /* Configure the RTC date register */
        if (RTC_SetDate(RTC_FORMAT_BIN, &RTC_DateStructure) == ERROR)
        {
            LOG_DEBUG("RTC Set Date failed...\r\n");
            return;
        }
        /* Set initinal Time*/
        RTC_TimeStructInit(&RTC_TimeStructure);
        /* Configure the RTC time register */
        if (RTC_ConfigTime(RTC_FORMAT_BIN, &RTC_TimeStructure) == ERROR)
        {
            LOG_DEBUG("RTC Set Time failed...\r\n");
            return;
        }
        BKP_WriteBkpData(BKP_DAT1, USER_WRITE_BKP_DAT1_DATA);
        LOG_DEBUG("RTC configure finished...\r\n");
    } else {
        LOG_DEBUG("RTC has been configured...\r\n");
    }

    RTC_GetDate(RTC_24HOUR_FORMAT, &RTC_DateStructure);
    RTC_GetTime(RTC_FORMAT_BIN, &RTC_TimeStructure);

    LOG_DEBUG("The current date(Week-Date-Month-Year)\r\n%0.2d-%0.2d-%0.2d-%0.2d\r\n",
              RTC_DateStructure.WeekDay,
              RTC_DateStructure.Date,
              RTC_DateStructure.Month,
              RTC_DateStructure.Year);
    LOG_DEBUG("The current time(Hour-Minute-Second)\r\n%0.2d:%0.2d:%0.2d\r\n",
              RTC_TimeStructure.Hours,
              RTC_TimeStructure.Minutes,
              RTC_TimeStructure.Seconds);
}


/**
 * @brief  RTC prescaler config.
 */
void RTC_PrescalerConfig(void)
{
    /* Configure the RTC data register and RTC prescaler */
    RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;
    RTC_InitStructure.RTC_SynchPrediv  = SynchPrediv;
    RTC_InitStructure.RTC_HourFormat   = RTC_24HOUR_FORMAT;
    /* Check on RTC init */
    if (RTC_Init(&RTC_InitStructure) == ERROR)
    {
        LOG_DEBUG("RTC Prescaler Config failed...\r\n");
    }
}

/**
 * @brief  Configures the RTC Source Clock Type.
 * @param Clk_Src_Type specifies RTC Source Clock Type.
 *   This parameter can be on of the following values:
 *     @arg RTC_CLK_SRC_TYPE_HSE128
 *     @arg RTC_CLK_SRC_TYPE_LSE
 *     @arg RTC_CLK_SRC_TYPE_LSI
 * @param Is_First_Cfg_RCC specifies Is First Config RCC Module.
 *   This parameter can be on of the following values:
 *     @arg true
 *     @arg false
 * @param Is_Rst_Bkp specifies Whether Reset The Backup Area
 *   This parameter can be on of the following values:
 *     @arg true
 *     @arg false
 */
void RTC_CLKSourceConfig(RTC_CLK_SRC_TYPE Clk_Src_Type, bool Is_First_Cfg_RCC, bool Is_Rst_Bkp)
{
    /* Enable the PWR clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
    /* Allow access to RTC */
    PWR_BackupAccessEnable(ENABLE);
    /* Reset Backup */
    if (true == Is_Rst_Bkp)
    {
        BKP_DeInit();
    }
    /* Disable RTC clock */
    RCC_EnableRtcClk(DISABLE);
    if (RTC_CLK_SRC_TYPE_HSE128 == Clk_Src_Type)
    {
        LOG_DEBUG("RTC_ClkSrc Is Set HSE128...\r\n");
        if (true == Is_First_Cfg_RCC )
        {
            /* Enable HSE */
            RCC_EnableLsi(DISABLE);
            RCC_ConfigHse(RCC_HSE_ENABLE);
            while (RCC_WaitHseStable() == ERROR)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_HSE_DIV128);
        }
        else
        {
            RCC_EnableLsi(DISABLE);
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_HSE_DIV128);
            /* Enable HSE */
            RCC_ConfigHse(RCC_HSE_ENABLE);
            while (RCC_WaitHseStable() == ERROR)
            {
            }
        }
        SynchPrediv  = 0x1E8; // 8M/128 = 62.5KHz
        AsynchPrediv = 0x7F;  // value range: 0-7F
    }
    else if (RTC_CLK_SRC_TYPE_LSE == Clk_Src_Type)
    {
        LOG_DEBUG("RTC_ClkSrc Is Set LSE...\r\n");
        if (true == Is_First_Cfg_RCC)
        {
            /* Enable the LSE OSC32_IN PC14 */
            RCC_EnableLsi(DISABLE); // LSI is turned off here to ensure that only one clock is turned on
#if (_TEST_LSE_BYPASS_)
            RCC_ConfigLse(RCC_LSE_BYPASS);
#else
            RCC_ConfigLse(RCC_LSE_ENABLE);
#endif
            while (RCC_GetFlagStatus(RCC_FLAG_LSERD) == RESET)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);
        }
        else
        {
            /* Enable the LSE OSC32_IN PC14 */
            RCC_EnableLsi(DISABLE);
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);
#if (_TEST_LSE_BYPASS_)
            RCC_ConfigLse(RCC_LSE_BYPASS);
#else
            RCC_ConfigLse(RCC_LSE_ENABLE);
#endif
            while (RCC_GetFlagStatus(RCC_FLAG_LSERD) == RESET)
            {
            }
        }
        SynchPrediv  = 0xFF; // 32.768KHz
        AsynchPrediv = 0x7F; // value range: 0-7F
    }
    else if (RTC_CLK_SRC_TYPE_LSI == Clk_Src_Type)
    {
        LOG_DEBUG("RTC_ClkSrc Is Set LSI...\r\n");
        if (true == Is_First_Cfg_RCC)
        {
            /* Enable the LSI OSC */
            RCC_EnableLsi(ENABLE);
            while (RCC_GetFlagStatus(RCC_FLAG_LSIRD) == RESET)
            {
            }
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSI);
        }
        else
        {
            RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSI);
            /* Enable the LSI OSC */
            RCC_EnableLsi(ENABLE);
            while (RCC_GetFlagStatus(RCC_FLAG_LSIRD) == RESET)
            {
            }
        }
        SynchPrediv  = 0x136; // 39.64928KHz
        AsynchPrediv = 0x7F;  // value range: 0-7F
    }
    else
    {
        LOG_DEBUG("RTC_ClkSrc Value is error...\r\n");
    }
    /* Enable the RTC Clock */
    RCC_EnableRtcClk(ENABLE);
    RTC_WaitForSynchro();
}

uint16_t MaxDate(uint16_t year, uint16_t month)
{
    const uint8_t maxdaylist[4] = {28, 29, 30, 31};
    year += 2000;
    uint8_t leap = 0;
    if (( (0 ==year % 4)&& (year % 100 != 0)) || (0 == year % 400)) {
        leap = 1;
    }
    switch(month) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        return 31;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        return 30;
        break;
    case 2:
        if(leap == 1) {
            return 29;
        } else {
            return 28;
        }
        break;
    default:
        NULL;
        break;
    }
    return 0;
}

